﻿using System;
using System.Collections.Generic;
using System.Text;
using AMS.DataAccess.Properties;
using System.Reflection;

namespace AMS.DataAccess.Sql
{
    /// <summary>
    /// 条件表式构造器
    /// </summary>
    internal class ConditionBuilder : SqlStatement, IConditionProvider<ConditionBuilder>
    {
        /// <summary>
        /// 获取已构造的表达式输出
        /// </summary>
        public StringBuilder Condition
        {
            get { return sbCondition; }
        }

        StringBuilder sbCondition = new StringBuilder();

        /// <summary>
        /// 添加一个条件
        /// </summary>
        /// <param name="expression">条件表达式，如果已经有条件表达式，则用 AND 将它们连接起来</param>
        public ConditionBuilder AddCondition(string expression)
        {
            if (sbCondition.Length == 0)
            {
                sbCondition.Append(CompactMode ? " " : "    ");
                NL(sbCondition.Append(expression));
            }
            else
            {
                sbCondition.Append(CompactMode ? " AND " : "    AND ");
                NL(sbCondition.Append(expression));
            }

            return this;
        }

        /// <summary>
        /// 添加一个条件
        /// </summary>
        /// <param name="condition">条件表达式，如果已经有条件表达式，则用 AND 将它们连接起来</param>
        public ConditionBuilder AddCondition(SqlCondition condition)
        {
            return AddCondition(condition.ToString());
        }

        /// <summary>
        /// 添加一个条件
        /// </summary>
        /// <param name="sql">条件表达式的左边</param>
        /// <param name="sql2">条件表达式的右边</param>
        public ConditionBuilder AddCondition(string sql, string sql2)
        {
            return AddCondition(sql + sql2);
        }

        /// <summary>
        /// 添加一个条件
        /// </summary>
        /// <param name="logicOperator">条件运算符，必需是 AND/OR, 需要自行处理换行</param>
        /// <param name="condition">条件</param>
        public ConditionBuilder AddCondition(string logicOperator, SqlCondition condition)
        {
            if (sbCondition.Length > 0)
            {
                sbCondition.Append(logicOperator);
            }
            else
            {
                sbCondition.Append(CompactMode ? " " : "    ");
            }

            NL(sbCondition.Append(condition.ToString()));
            return this;
        }

        /// <summary>
        /// 将对象的主键添加为 where 条件
        /// </summary>
        /// <param name="businessObject">业务对象</param>
        public void AddPrimaryKeyFieldAsCondition(object businessObject)
        {
            AddPrimaryKeyFieldAsCondition(businessObject, businessObject.GetType());
        }

        /// <summary>
        /// 将对象的主键添加为 where 条件
        /// </summary>
        /// <param name="businessObject">业务对象</param>
        /// <param name="objectType">对象的实际类型</param>
        public void AddPrimaryKeyFieldAsCondition(object businessObject, Type objectType)
        {
            if (objectType.IsInterface)
            {
                throw new ArgumentException(Resources.InterfaceTypeNotSupport);
            }

            if (businessObject.GetType() != objectType && !businessObject.GetType().IsSubclassOf(objectType))
            {
                throw new ArgumentException(Resources.TypeOfBOCanOnlyBeTheGivenType);
            }

            ObjectInfo objectInfo = ObjectUtils.GetBOObjectInfo(objectType);
            foreach (PropertyInfo property in objectInfo.PKFields)
            {
                object[] attributes = property.GetCustomAttributes(true);
                foreach (Attribute attr in attributes)
                {
                    if (attr is DBColumnAttribute)
                    {
                        DBColumnAttribute dbcolumnAttr = attr as DBColumnAttribute;
                        string columnName = dbcolumnAttr.Column;
                        if (string.IsNullOrEmpty(columnName))
                        {
                            columnName = property.Name;
                        }
                        object retValue = ObjectUtils.GetPropertyValue(businessObject, property);
                        if (retValue == null || retValue == DBNull.Value)
                        {
                            AddCondition(string.Format("{0} = NULL", columnName));
                        }
                        else
                        {
                            TypeCode code = Convert.GetTypeCode(retValue);
                            switch (code)
                            {
                                case TypeCode.String:
                                    AddCondition(new SqlCondition(columnName, Convert.ToString(retValue)));
                                    break;

                                case TypeCode.Byte:
                                case TypeCode.Int16:
                                case TypeCode.Int32:
                                case TypeCode.SByte:
                                case TypeCode.Single:
                                    AddCondition(string.Format("{0} = {1}", columnName, Convert.ToInt32(retValue)));
                                    break;

                                case TypeCode.UInt16:
                                case TypeCode.UInt32:
                                    AddCondition(string.Format("{0} = {1}", columnName, Convert.ToUInt32(retValue)));
                                    break;

                                case TypeCode.DateTime:
                                    AddCondition(new SqlCondition(
                                        columnName,
                                        Convert.ToDateTime(retValue),
                                        ((DBDateTimeAttribute)attr).DateTimeType == DateTimeType.DateTime));
                                    break;

                                case TypeCode.Boolean:
                                    AddCondition(string.Format(
                                        "{0} = {1}",
                                        columnName,
                                        ((bool)retValue) ? "1" : "0"));
                                    break;

                                case TypeCode.Int64:
                                    AddCondition(string.Format("{0} = {1}", columnName, Convert.ToInt64(retValue)));
                                    break;

                                case TypeCode.Decimal:
                                    AddCondition(string.Format("{0} = {1}", columnName, Convert.ToDecimal(retValue)));
                                    break;
                                case TypeCode.Double:
                                    AddCondition(string.Format("{0} = {1}", columnName, Convert.ToDouble(retValue)));
                                    break;

                                case TypeCode.UInt64:
                                    AddCondition(string.Format("{0} = {1}", columnName, Convert.ToUInt64(retValue)));
                                    break;

                                case TypeCode.Object:
                                    AddCondition(new SqlCondition(columnName, retValue.ToString()));
                                    break;

                                default:
                                    throw new NotSupportedException(Resources.DataTypeNotSupportedFormat);
                            }
                        }

                        break;
                    }
                }
            }
        }
    }
}
